#include "Renderer.h"

// this function initializes and prepares Direct3D for use
void Renderer::initD3D(HWND hWnd)
{
	d3d = Direct3DCreate9(D3D_SDK_VERSION);

    D3DPRESENT_PARAMETERS d3dpp;

    ZeroMemory(&d3dpp, sizeof(d3dpp));
    d3dpp.Windowed = TRUE;
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    d3dpp.hDeviceWindow = hWnd;
    d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
    d3dpp.BackBufferWidth = SCREEN_WIDTH;
    d3dpp.BackBufferHeight = SCREEN_HEIGHT;
	d3dpp.EnableAutoDepthStencil = TRUE;
	d3dpp.AutoDepthStencilFormat = D3DFMT_D16;

    // create a device class using this information and the info from the d3dpp stuct
    d3d->CreateDevice(D3DADAPTER_DEFAULT,
                      D3DDEVTYPE_HAL,
                      hWnd,
                      D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                      &d3dpp,
                      &d3ddev);

    init_graphics();    // call the function to initialize the triangle

    d3ddev->SetRenderState(D3DRS_LIGHTING, FALSE);    // turn off the 3D lighting
	d3ddev->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
	d3ddev->SetRenderState(D3DRS_ZENABLE, TRUE);	  // turn on the z-buffer
}

// this is the function that puts the 3D models into video RAM
void Renderer::init_graphics(void)
{
	// create the vertices using the CUSTOMVERTEX struct
    CUSTOMVERTEX vertices[] =
    {
		//vertices of a basic equilateral triangle
        { 2.0f, -2.0f, 0.0f, D3DCOLOR_XRGB(0, 0, 255), },
        { 2.0f, 2.0f, 0.0f, D3DCOLOR_XRGB(0, 0, 255), },
        { -2.0f, 0.0f, 0.0f, D3DCOLOR_XRGB(0, 0, 255), },

		//vertices for a square
		{ -1.0f, 1.0f, 0.0f, D3DCOLOR_XRGB(255, 0, 0), },
        { 1.0f, 1.0f, 0.0f, D3DCOLOR_XRGB(255, 0, 0), },
		{ -1.0f, -1.0f, 0.0f, D3DCOLOR_XRGB(255, 0, 0), },
		{ 1.0f, -1.0f, 0.0f, D3DCOLOR_XRGB(255, 0, 0), },

    };

    // create a vertex buffer interface called v_buffer
    d3ddev->CreateVertexBuffer(DIM(vertices)*sizeof(CUSTOMVERTEX),
                               0,
                               CUSTOMFVF,
                               D3DPOOL_MANAGED,
                               &v_buffer,
                               NULL);

    VOID* pVoid;    // a void pointer

    // lock v_buffer and load the vertices into it
    v_buffer->Lock(0, 0, (void**)&pVoid, 0);
    memcpy(pVoid, vertices, sizeof(vertices));
    v_buffer->Unlock();
}

//this function renders a single frame
void Renderer::render_frame(vector<MovingObject*> objs)
{
	d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
	d3ddev->Clear(0, NULL, D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);

    d3ddev->BeginScene();

    // select which vertex format we are using
    d3ddev->SetFVF(CUSTOMFVF);

    // SET UP THE PIPELINE

	//this is practically psuedo code
	//for each of the MovingObject pointers within the vector called "objs", do the following
	for each(MovingObject* obj in objs)
	{
		//says to DirectX "Okay, here's one of my object"
		//fortunately, MovingObject keeps track of the DirectX Translation Matrix, so we don't have to compute it here
		//we just give it the matrix multiplied with the angle it needs to be rotated
		d3ddev->SetTransform(D3DTS_WORLD, &(/*obj->d3dAngle **/ obj->d3dPosition));

		//after SetTransform() is called, DirectX says "Alright... I see the object". Now we say "Cool, draw it"
		//if the object tells you it's a triangle, draw the triangle primitive that I told you about earlier
		if(obj->getShape() == TRIANGLE)	//keep in mind DirectX doesn't naturally know what "TRIANGLE" means, it's an enum I made in enums.h
		{
		d3ddev->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1);	// copy the vertex buffer to the back buffer
								//these parameters are what create the triangle
		}
		//if it's not a triangle, check to see if it's a square, if so, draw it as a square, which I defined for you earlier
		else if(obj->getShape() == SQUARE)
		{
			d3ddev->DrawPrimitive(D3DPT_TRIANGLESTRIP, 3, 2);
									//these parameters are what create the square, see the following comment for their meanings
		}
		//for whatever reason it's not marked to be a triangle or square, make it a triangle regardless
		else
			d3ddev->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1);
		//the first argument of DrawPrimitive is what type of shape, 
		//the second is what number of the vertex buffer to start drawing from
		//and the third is how many primitives. We can make a triangle with one, and a square with two
	}

	//d3ddev->SetTransform(D3DTS_WORLD, &(obj.d3dTarget));
	

    D3DXMATRIX matView;    // the view transform matrix

    D3DXMatrixLookAtLH(&matView,
                       &D3DXVECTOR3 (0.0f, 0.0f, 100.0f),    // the camera position
                       &D3DXVECTOR3 (0.0f, 0.0f, 0.0f),    // the look-at position
                       &D3DXVECTOR3 (0.0f, 1.0f, 0.0f));    // the up direction

    d3ddev->SetTransform(D3DTS_VIEW, &matView);    // set the view transform to matView

    D3DXMATRIX matProjection;     // the projection transform matrix

    D3DXMatrixPerspectiveFovLH(&matProjection,
                               D3DXToRadian(45),    // the horizontal field of view
                               (FLOAT)SCREEN_WIDTH / (FLOAT)SCREEN_HEIGHT, // aspect ratio
                               1.0f,    // the near view-plane
                               100.0f);    // the far view-plane

    d3ddev->SetTransform(D3DTS_PROJECTION, &matProjection);    // set the projection

    // select the vertex buffer to display
    d3ddev->SetStreamSource(0, v_buffer, 0, sizeof(CUSTOMVERTEX));



    d3ddev->EndScene();

    d3ddev->Present(NULL, NULL, NULL, NULL);
}

void Renderer::cleanD3D(void)
{
	v_buffer->Release();    // close and release the vertex buffer
    d3ddev->Release();    // close and release the 3D device
    d3d->Release();    // close and release Direct3D
}